﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net.Nntp
{
    using System;
    using System.IO;
    using System.Text;

    /// <summary>Encapsulates the overview information of an article, see
    /// <a href="http://www.ietf.org/rfc/rfc3977.txt">RFC 3977</a> 8.3.2.</summary>
    /// <threadsafety static="true" instance="false"/>
    public sealed class OverInfo
    {
        private static readonly byte[] SubjectName = Encoding.UTF8.GetBytes("Subject: ");
        private static readonly byte[] FromName = Encoding.UTF8.GetBytes("From: ");
        private static readonly byte[] DateName = Encoding.UTF8.GetBytes("Date: ");
        private static readonly byte[] MessageIdName = Encoding.UTF8.GetBytes("Message-Id: ");
        private static readonly byte[] ReferencesName = Encoding.UTF8.GetBytes("References: ");
        private static readonly byte[] LineBreak = Encoding.UTF8.GetBytes("\r\n");

        private readonly long number;
        private readonly byte[] headerOverview;
        private readonly long bytes;
        private readonly long lines;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>Gets the number of the article, can be 0.</summary>
        public long Number
        {
            get { return this.number; }
        }

        /// <summary>Gets the part of the message header contained in the overview.</summary>
        public MemoryStream HeaderOverview
        {
            get { return new MemoryStream(this.headerOverview, 0, this.headerOverview.Length, false); }
        }

        /// <summary>Gets the :bytes metadata item.</summary>
        public long Bytes
        {
            get { return this.bytes; }
        }

        /// <summary>Gets the :lines metadata item.</summary>
        public long Lines
        {
            get { return this.lines; }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal OverInfo(
            long number,
            ArraySegment<byte> subject,
            ArraySegment<byte> from,
            ArraySegment<byte> date,
            ArraySegment<byte> messageId,
            ArraySegment<byte> references,
            long bytes,
            long lines)
        {
            this.number = number;

            using (var stream = new MemoryStream(
                (subject.Count == 0 ? 0 : (SubjectName.Length + subject.Count + LineBreak.Length)) +
                (from.Count == 0 ? 0 : (FromName.Length + from.Count + LineBreak.Length)) +
                (date.Count == 0 ? 0 : (DateName.Length + date.Count + LineBreak.Length)) +
                (messageId.Count == 0 ? 0 : (MessageIdName.Length + messageId.Count + LineBreak.Length)) +
                (references.Count == 0 ? 0 : (ReferencesName.Length + references.Count + LineBreak.Length))))
            {
                if (subject.Count != 0)
                {
                    Write(stream, SubjectName);
                    WriteLine(stream, subject);
                }

                if (from.Count != 0)
                {
                    Write(stream, FromName);
                    WriteLine(stream, from);
                }

                if (date.Count != 0)
                {
                    Write(stream, DateName);
                    WriteLine(stream, date);
                }

                if (messageId.Count != 0)
                {
                    Write(stream, MessageIdName);
                    WriteLine(stream, messageId);
                }

                if (references.Count != 0)
                {
                    Write(stream, ReferencesName);
                    WriteLine(stream, references);
                }

                this.headerOverview = stream.GetBuffer();
            }

            this.bytes = bytes;
            this.lines = lines;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private static void Write(Stream stream, byte[] bytes)
        {
            stream.Write(bytes, 0, bytes.Length);
        }

        private static void WriteLine(Stream stream, ArraySegment<byte> bytes)
        {
            stream.Write(bytes.Array, bytes.Offset, bytes.Count);
            Write(stream, LineBreak);
        }
    }
}
